package edu.siu.math.egut.main;

import java.io.IOException;
import java.util.Scanner;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Unipotent;
/**
 * DCA stands for Double Coset Analysis.  The set up is this.  Let G be a split connected  
 * simply connected algebraic group.  For EGUT, we assume G is simple and of one of the 
 * five exceptional types, but the theory here is general.  As always, we assume G to be equipped with 
 * a choice of split maximal torus T (used to define roots) Borel subgroup B=TN (used to define 
 * simple roots and positive roots, as well as standard parabolics) and a system of 
 * epinglage, chosen so that the structure constants match the algorithm in the paper of 
 * Gilkey and Seitz, with the numbering used in LiE (which is different from that of Gilkey-Seitz
 * in the case of G2).
 * 
 * We consider two parabolics, P=MU and Q=LV, and a psi-factor psi on U.  Let us assume that psi
 * is in fact a character of U (though no checking of this is performed).  Let S < M denote the 
 * stabilizer of psi. The purpose of this program is to help with the analysis of the (U, psi) 
 * Fourier coefficient of an Eisenstein series induced from the parabolic Q.  This Fourier 
 * coefficient breaks naturally into terms indexed by Q\G/SU.  Our goal is to analyze these 
 * terms.
 * 
 * As a first approximation, we may consider Q\G/P, which is represented by minimal-length elements
 * of the Weyl group.  Based on experience, we consider only the double coset containing the longest 
 * element of this Weyl group.  The representative for this double coset will be denoted w0, and 
 * may be computed using the LiE command  "lr_reduce(qVector2, long_word, pVector2)," where qVector2 
 * and pVector2 list the simple roots of G that lie in L and M respectively.  The command lr_reduce 
 * is not built into LiE but is defined in the manual and in the EGUT LiE initfile.
 * 
 * Let P1 = M \cap w_0^{-1} Q w_0.  It is a standard parabolic of M.  Let P_\psi =M_\psi U_\psi
 * denote the largest standard parabolic of M such that M_\psi < S.  Finally, for any element 
 * nu of W, let N_nu = N \cap nu^{-1} \overline{N} \nu, where \overline{N} denotes the maximal 
 * unipotent opposite to N.  
 * 
 * We need to analyze P1\M/S.  One may show that every element contains a representative of the 
 * form nu0 delta, where nu0 is an element of N_M(T) representing an element of W(M,T) which 
 * is of minimal length in W(M1, T)\W(M,T)/W(M_psi, T), and delta is an element on N_{nu0}.
 *
 * 
 * Once the relevant data is extracted from the args, the following steps are performed for each nu0.
 * 
 * (1) Display nu0.
 * (2) Compute and display the set of positive roots made negative by nu0.  (By definition, they 
 * will all be roots of T in M.)  This can be computed using the EGUT LiE initfile command "goesDown( nu0)."
 * (3) Compute  the set of roots of T in U left positive by w0nu0, and display a side-by-side comparison
 * of these roots and the roots that they are mapped to, with those mapped into L shown first and those
 * mapped into V shown second.  The set may be computed using the EGUT LiE initfile command "goesUp( w0^nu0, U)."
 * It is also image( inverse(nu0), goesUp( w0, U)).  Comparisons may be obtained using the "compare" 
 * command, after intersecting with L and V respectively.
 * (4) Compute and display the set of roots of T in U made negative by w0nu0. This may be computed using the 
 * EGUT LiE initfile commands "goesDown( w0^nu0,  U)," "image( inverse( nu0), goesDown( w0))," or 
 * complement( goesDown( w0^nu0), goesDown( nu0))," reflecting two alternate descriptions of the same set:  it 
 * is the image under nu0^{-1} of the set of positive roots made negative by w0, and it is the set of roots 
 * made negative by w0nu0, but not by nu0. 
 * (5) Compute and display a certain character (or more general psi-factor if the original psi was not a character)
 *  \psi_delta(u') of U' := U\cap (w_0nu0)^{-1} N w_0nu0 (which is the product of the one-dimensional unipotent subgroups 
 *  indexed by the set of roots computed in (3)).  This character is defined in terms of an element delta of N_nu0 
 *  (which is the product of the one-dimensional unipotent subgroups indexed by the set of roots computed in (2)).
 *  To be precise delta^{-1} u' \delta is in U and \psi_\delta(u') = \psi( delta^{-1} u' \delta).
 * 
 * @author Joseph Hundley
 *
 */
public class DCA {
    private static final OutputPref ERR = OutputPref.ERR;
    private static final OutputPref SHOW=OutputPref.SHOW;
    private static final OutputPref HIDE = OutputPref.HIDE;
    private static LiE lie = new LiE();

    /**
     * @param args parabolic P, parabolic Q, polynomial psi, parabolic P2 of the Levi of P, name of exceptional group 
     */
    public static void main(String[] args) {
	
	if( args.length <5){
	    System.out.print("Not enough information.  Need:\n " +
	    		"\t* Parabolic P, such that the Fourier coefficient is taken along the\n\tunipotent radical of P,\n" +
	    		"\t* Parabolic Q, such that Eisenstein is induced from Q,\n" +
	    		"\t* a polynomial psi factor,\n" +
	    		"\t* Parabolic R, of the Levi of M such that you want to consider orbits\n\tin M(P)/R.\n" +
	    		"The first two parabolics should be specified by listing the simple roots in the \nunipotent radical, " +
	    		"and last one by listing the roots in the Levi.\n" +
	    		"Example: [2] [1] u11222221+u22343210 [4,7] E8\n");
	    System.exit(1);
	    
	}
	
	
	String pString =args[0];
	String qString = args[1];
	
	Polynomial psi = Polynomial.create(1);
	    try {
		psi = Polynomial.read(args[2]);
	    } catch (PolynomialReadException e1) {
		System.out.println("Problem reading psi.");
		e1.printStackTrace();
		System.exit(1);
	    }
	Polynomial psi1= psi;
	String p2String = args[3];
	String groupName = args[4];
	
	
	
	    
	try {
	    
	    
	    
		// set default group   
	    lie.exec( "setdefault "+groupName, ERR);
	    lie.exec("p = "+pString, ERR);
	    lie.exec("q="+qString, ERR);
	    // get w0.
	    lie.exec("w0 = lr_reduce(complement("+qString+"),long_word, complement("+pString+"))", ERR);
	    say("Long element of Q\\G/P (denote by w0)");
	    lie.exec("w0",SHOW);
	    say("w0^{-1}Qw0 \\cap M(P)");
	    lie.exec( "p1=complement(complement(p), levi_para( w0,"+qString+","+pString+"))", ERR);
	    lie.exec("p1", SHOW);
	    int[][] m= Algorithms.readLiEMatrix(
		    lie.exec(
			    "levi_dc_matrix(p1,"+ p2String+","+pString+")", HIDE));
	    for( int i = 0 ; i < m.length; i++){
		// define nu0String 
		String nu0String = "W_word("+lieVectorString(m[i])+")";
		// define  nu0 in the LiE process
		lie.exec("nu0 = "+nu0String, ERR);
		
		// display nu0
		System.out.println("nu0 = ");
		lie.exec("nu0", SHOW);

		analyze( nu0String, pString, qString, psi, lie);
		
		 // reset psi 
		psi =psi1;
		
		
		
		

	    }
	    

	    
	} catch (IOException e) {
	    System.out.println("Problem starting LiE.");
	    e.printStackTrace();
	    System.exit(1);
	} catch (Exception e) {
	    System.out.println("Exception encountered.  Possible cause:  problem reading LiE output.");

	    e.printStackTrace();
	}



	
    }

    
    /**
     * @param nu0String defining the Weyl element nu0
     * @param pString defining the parabolic P
     * @param qString defining the parabolic Q
     * @param psi defining the character psi
     * @param lie a LiE process
     * @throws IOException usually as a result of calling lie.exec
     * @throws Exception various "read" commands
     */
    public static void analyze( String nu0String, String pString, String qString, Polynomial psi, LiE lie )  throws IOException, Exception{
	
	// define  nu0 in the LiE process
	lie.exec("nu0 = "+nu0String, ERR);

	    // display goesDown( nu0), and collect it into an int[][]
	    say("Positive roots sent down by nu0:");
	String goesDown = lie.exec("goesDown( nu0) ", SHOW);
	
	// for convenience, define U.  We call it u in LiE because 'U' is not allowed by LiE.
	lie.exec("u = unipRad("+pString+")", ERR);
	
	// compute goesUp( w0^nu0, u), and collect it into an int[][].  do not 
	// display:  we wish to display more precise information using compare.
	lie.exec("u1 = goesUp( w0^nu0, u)", ERR);
	lie.exec("u2 = goesDown(w0^nu0, u)", ERR);
	int[][] uRoots = Algorithms.readLiEMatrix(lie.exec("u1^u2", HIDE));
	int[][] uPrimeRoots = Algorithms.readLiEMatrix(lie.exec("u1", HIDE));
	
	say("Roots in U sent up by w0^nu0-- those sent into the Levi first.");
	//display more precise information using compare.  first what is mapped up and into the levi, L of Q
	lie.exec("compare( w0^nu0, intersection( u, preimage( w0^nu0, levi("+qString+"))))", SHOW);
	// and then what is mapped into the unipotent radical V.
	lie.exec("compare( w0^nu0, intersection( u, preimage( w0^nu0, unipRad("+qString+"))))", SHOW);
	
	if( !goesDown.contains("null")){
	    int[][] deltaRoots = Algorithms.readLiEMatrix(goesDown);
		
	Unipotent delta  = new Unipotent(deltaRoots, "d"); 
	Unipotent u = new Unipotent(uRoots, "u");
	
	//multiply u by d on the left 
	u = Unipotent.multiply(delta, u);

	
	/* 
	 * Rearrange into the form u' delta u" where 
	 * w0 nu0 maps u' into N and u" into N^-.
	 */
	int count = 1;
	while(u.indexOf(uPrimeRoots, u.indexOf(deltaRoots))!= -1){
	    
	    try{
	    u.pushLeft(u.indexOf(uPrimeRoots, u.indexOf(deltaRoots)));
	    }
	    catch(Exception e){
		System.out.println(u);
		System.out.println("index of delta roots: " +u.indexOf(deltaRoots)+ "\tSubsequent index of uPrimeRoots: "+ u.indexOf(uPrimeRoots, u.indexOf(deltaRoots))+ "\tLength: " + u.getNumberOfTerms());
		System.exit(1);
	    }
	    
	    u.cleanUp();
	    u.clearZeros();
	    psi = u.chOfVars(psi);
	    count++;
	    
	    
	}
	
	
	}
	System.out.println(psi);

	
    }
    
    /**
     * Just to avoid typing System.out.println over and over...
     * @param string String
     */
    public static void say(final String string) {
	System.out.println(string);
    }

 
    private static String lieVectorString(int[] is) {
	StringBuilder sb = new StringBuilder("[");
	for(int i = 0 ; i <is.length; i++){
	    sb.append(Integer.toString(is[i]));
	    if( i < is.length-1)
		sb.append(",");
	    
	    
	}
	sb.append("]");
	
	return sb.toString();
    }

    /**
     * This method facilitates interaction with LiE by supplying the letter (E, G, or F) which 
     * appears in the name of the unique exceptional group of the given rank.
     * 
     * type(2) = G
     * type(4) = F
     * type(6,7, or 8) = E
     * type( any other number ) = null.
     * 
     * @param rank an integer:  should be 2,4,6,7, or 8.
     * @return the letter in the name of the unique excpetional group of the given rank
     */
    public static String type(final int rank) {
	switch (rank){
	case 2: return "G";
	case 4: return "F";
	case 6: return "E";
	case 7: return "E";
	case 8: return "E";
	default: return null;
	}
	    
    
    }

}
